﻿namespace UDKDevKitVSX
{
    using System;
    using System.Collections.Concurrent;
    using System.Collections.Generic;
    using System.Diagnostics;
    using UDKDevKitVSX.Interfaces;

    public class UdkConduit : IUdkConduit, IDisposable
    {
        private IList<AddWatchRecord>[] watchListBuffers;
        private IUdkConduit vsxConduit;

        public UdkConduit(IUdkCommandInterface udkCommandInterface)
        {
            if (udkCommandInterface == null)
            {
                Debug.Fail("ArgumentNullException - udkCommandInterface");
                throw new ArgumentNullException("udkCommandInterface");
            }

            this.UDKCommandInterface = udkCommandInterface;
            this.watchListBuffers = new List<AddWatchRecord>[]
            {
                new List<AddWatchRecord>(40000),
                new List<AddWatchRecord>(40000),
                new List<AddWatchRecord>(40000),
            };
        }

        internal IUdkCommandInterface UDKCommandInterface { get; private set; }
        
        protected IUdkConduit VsxConduit 
        {
            get
            {
                if (this.vsxConduit == null)
                {
                    this.vsxConduit = new VSXShellController(this.UDKCommandInterface);
                }

                return this.vsxConduit;
            }
        }

        public void Dispose()
        {
            if (this.vsxConduit != null)
            {
                ((IDisposable)this.VsxConduit).Dispose();
            }
        }

        #region IUdkConduit Members

        public void ShowDllForm()
        {
            Debug.WriteLine("call::ShowDllForm");

            this.VsxConduit.ShowDllForm();
        }

        public void BuildHierarchy()
        {
            Debug.WriteLine("call::BuildHierarchy");
        }

        public void ClearHierarchy()
        {
            Debug.WriteLine("call::ClearHierarchy");
        }

        public void AddClassToHierarchy(string className)
        {
            Debug.WriteLine(String.Format("call::AddClassToHierarchy = {0}", className));
        }

        public void ClearWatch(int watchType)
        {
            Debug.WriteLine(String.Format("call::ClearWatch = {0}", watchType));

            this.ClearWatch(watchType);
        }

        public void ClearAWatch(int watchType)
        {
            Debug.WriteLine(String.Format("call::ClearAWatch = {0}", watchType));

            this.watchListBuffers[watchType].Clear();
        }

        public int AddAWatch(int watchType, int parentIndex, string varName, string varValue)
        {
            var watchListBuffer = this.watchListBuffers[watchType];
            watchListBuffer.Add(
                new AddWatchRecord()
                {
                    ParentIndex = parentIndex,
                    VarName = varName,
                    VarValue = varValue,
                }
            );

            return watchListBuffer.Count;
        }

        public void LockList(int watchList)
        {
            Debug.WriteLine(String.Format("call::LockList = {0}", watchList));
        }

        public void UnlockList(int watchList)
        {
            Debug.WriteLine(String.Format("call::UnlockList = {0}", watchList));

            // here we send all into to VSSide
            this.VsxConduit.AddWatches(watchList, this.watchListBuffers[watchList]);
        }

        public void AddBreakpoint(string className, int lineNo)
        {
            Debug.WriteLine(String.Format("call::AddBreakpoint ClassName: {0}, Line: {1}", className, lineNo));
        }

        public void RemoveBreakpoint(string className, int lineNo)
        {
            Debug.WriteLine(String.Format("call::RemoveBreakpoint ClassName: {0}, Line: {1}", className, lineNo));
        }

        public void EditorLoadClass(string className)
        {
            Debug.WriteLine(String.Format("call::EditorLoadClass ClassName: {0}", className));

            this.VsxConduit.EditorLoadClass(className);
        }

        public void EditorGotoLine(int lineNo, int highlight)
        {
            Debug.WriteLine(String.Format("call::EditorGotoLine Line: {0}, Highlight: {1}", lineNo, highlight));

            this.VsxConduit.EditorGotoLine(lineNo, highlight);
        }

        public void AddLineToLog(string text)
        {
            Debug.WriteLine(String.Format("call::AddLineToLog '{0}'", text));

            if (this.vsxConduit == null)
            {
                Debug.WriteLine(String.Format("WORNING! vsxConduit is null, call::AddLineToLog '{0}'", text));
                return;
            }

            this.VsxConduit.AddLineToLog(text);

            if (String.Compare(text, "Log: Detaching UnrealScript Debugger (currently detached)", true) == 0)
            {
                this.ShutDown();
            }
        }

        public void CallStackClear()
        {
            Debug.WriteLine(String.Format("call::CallStackClear"));

            this.VsxConduit.CallStackClear();
        }

        public void CallStackAdd(string callStackEntry)
        {
            Debug.WriteLine(String.Format("call::CallStackAdd = {0}", callStackEntry));

            this.VsxConduit.CallStackAdd(callStackEntry);
        }

        public void SetCurrentObjectName(string objectName)
        {
            Debug.WriteLine(String.Format("call::SetCurrentObjectName = {0}", objectName));
        }

        public void DebugWindowState(int stateCode)
        {
            Debug.WriteLine(String.Format("call::DebugWindowState = {0}", stateCode));
        }

        public void AddWatches(int watchList, IList<AddWatchRecord> properties)
        {
            throw new InvalidOperationException("Can't call this function from here");
        }

        public void ShutDown()
        {
            Debug.WriteLine("call::ShutDown");

            if (this.vsxConduit != null)
            {
                this.VsxConduit.ShutDown();
                this.vsxConduit = null;
            }
            else
            {
                Debug.WriteLine("call::ShutDown - vsxConduit is null");
            }
        }

        #endregion

        public static IUdkConduit Create(IUdkCommandInterface udkCommandInterface)
        {
            return new UdkConduit(udkCommandInterface);
        }
    }
}